package cn.demoncat.util.websocket.config;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

import cn.demoncat.util.lang.LongUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.LongConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import cn.demoncat.util.web.TokenUtil;
import cn.demoncat.util.web.entity.TokenPrincipal;
import cn.demoncat.util.websocket.util.WebSocketStompUtil;

/**
 * WebSocket消息代理配置（STOMP协议）
 * 
 * @author 延晓磊
 *
 * @since 2020年3月3日
 */
// 开启WebSocket，使用STOMP协议
@EnableWebSocketMessageBroker
@Configuration
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {
	
	private static final Logger LOG = LoggerFactory.getLogger(WebSocketStompConfig.class);

	/**
	 * 注册端点
	 * <pre>
	 * 前端连接：
	 * var socket = new SockJS("http://localhost:8080/stomp");
	 * var client = Stomp.over(socket);
	 * client.connect({}, connectCallback, errorCallback);
	 * </pre>
	 * @param registry
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年3月3日
	 */
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		// 注册端点，指定PATH
		registry.addEndpoint("/stomp")
				// 允许所有来源Origin
				.setAllowedOrigins("*")
				// 使用SockJS协议，实现浏览器兼容
				.withSockJS();
	}

	/**
	 * 配置消息代理（消息传输通道）
	 * 
	 * @param registry
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年3月3日
	 */
	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {
		// 开启代理（指定前端订阅主题的PATH）：点对点 /queue，广播 /topic
		registry.enableSimpleBroker("/queue", "/topic");
		// 点对点订阅主题的前缀：前端订阅"/user/queue/position"，服务端发送至 "/queue/position"
		registry.setUserDestinationPrefix("/user/");
	}

	/**
	 * 配置客户端请求通道
	 * 
	 * 1、拦截连接请求，设置会话用户
	 * 
	 * @param registration
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年3月4日
	 */
	 @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
		 // 通道拦截
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
            	// 获取STOMP协议请求头
                StompHeaderAccessor accessor = WebSocketStompUtil.getAccess(message);
                // 连接请求
                if (StompCommand.CONNECT.equals(accessor.getCommand())) {
                	// 获取Token
                	String token = accessor.getFirstNativeHeader(TokenUtil.TOKEN_KEY);
                	// 创建用户信息
                	TokenPrincipal user = new TokenPrincipal();
                	user.setSessionId(accessor.getSessionId());
                	if (StringUtils.isBlank(token)) {
                		user.setToken(StringConstant.EMPTY);
                		user.setUserId(LongConstant.N0);
					}else {
						user.setToken(StringUtil.toEmpty(TokenUtil.getClientToken(token)));
	            		user.setUserId(LongUtil.toEmpty(TokenUtil.getTokenUser(token)));
					}
					LOG.info("接收WebSocket-STOMP连接：user = {}, token = {}, session = {}", user.getUserId(), user.getToken(), user.getSessionId());
                    // 保存会话用户
                	accessor.setUser(user);
                }
                return message;
            }
        });
    }
}
